#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
事务性处理框架
确保混淆操作的原子性、一致性、隔离性和持久性（ACID）
"""

import os
import json
import time
import shutil
import threading
from typing import Dict, List, Any, Optional, Callable, Set
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
from pathlib import Path

from ..utils_module.logger import ObfuscationLogger


class TransactionState(Enum):
    """事务状态"""
    PENDING = "pending"
    ACTIVE = "active"
    COMMITTING = "committing"
    COMMITTED = "committed"
    ABORTING = "aborting"
    ABORTED = "aborted"
    FAILED = "failed"


class IsolationLevel(Enum):
    """隔离级别"""
    READ_UNCOMMITTED = 1  # 读未提交
    READ_COMMITTED = 2    # 读已提交
    REPEATABLE_READ = 3   # 可重复读
    SERIALIZABLE = 4      # 串行化


@dataclass
class Operation:
    """操作记录"""
    op_id: str
    op_type: str  # create, update, delete, rename
    target: str  # 文件路径或资源标识
    old_value: Any = None
    new_value: Any = None
    metadata: Dict[str, Any] = field(default_factory=dict)
    timestamp: float = field(default_factory=time.time)
    rollback_data: Optional[bytes] = None


@dataclass
class Transaction:
    """事务对象"""
    tx_id: str
    state: TransactionState = TransactionState.PENDING
    isolation_level: IsolationLevel = IsolationLevel.READ_COMMITTED
    operations: List[Operation] = field(default_factory=list)
    created_time: float = field(default_factory=time.time)
    start_time: Optional[float] = None
    commit_time: Optional[float] = None
    locks: Set[str] = field(default_factory=set)
    savepoints: Dict[str, int] = field(default_factory=dict)
    metadata: Dict[str, Any] = field(default_factory=dict)


@dataclass
class TransactionLog:
    """事务日志"""
    log_id: str
    tx_id: str
    action: str  # begin, commit, rollback, checkpoint
    timestamp: float
    details: Dict[str, Any] = field(default_factory=dict)


class TransactionManager:
    """事务管理器"""

    def __init__(self, logger: ObfuscationLogger = None,
                 workspace_dir: str = None,
                 enable_wal: bool = True):
        """
        初始化事务管理器

        Args:
            logger: 日志记录器
            workspace_dir: 工作空间目录
            enable_wal: 是否启用预写日志(Write-Ahead Logging)
        """
        self.logger = logger
        self.enable_wal = enable_wal

        # 工作空间
        if workspace_dir:
            self.workspace_dir = Path(workspace_dir)
        else:
            self.workspace_dir = Path.cwd() / ".tx_workspace"

        self.workspace_dir.mkdir(exist_ok=True)

        # 事务存储
        self.transactions: Dict[str, Transaction] = {}
        self.active_transactions: Set[str] = set()
        self.tx_lock = threading.RLock()

        # 锁管理
        self.resource_locks: Dict[str, threading.RLock] = {}
        self.lock_holders: Dict[str, str] = {}  # 资源 -> 事务ID
        self.lock_manager = threading.RLock()

        # 事务日志
        self.tx_logs: List[TransactionLog] = []
        self.log_file = self.workspace_dir / "transaction.log"

        # 备份管理
        self.backup_dir = self.workspace_dir / "backups"
        self.backup_dir.mkdir(exist_ok=True)

        # 恢复点
        self.checkpoints: List[str] = []

        # 事务计数器
        self.tx_counter = 0

        # 加载历史日志
        if enable_wal:
            self._load_transaction_log()

        if logger:
            logger.log_operation("事务管理器", f"初始化完成: WAL={enable_wal}")

    def begin_transaction(self, isolation_level: IsolationLevel = IsolationLevel.READ_COMMITTED) -> str:
        """
        开始新事务

        Args:
            isolation_level: 隔离级别

        Returns:
            事务ID
        """
        with self.tx_lock:
            self.tx_counter += 1
            tx_id = f"tx_{self.tx_counter}_{int(time.time())}"

            tx = Transaction(
                tx_id=tx_id,
                isolation_level=isolation_level
            )

            self.transactions[tx_id] = tx
            tx.state = TransactionState.ACTIVE
            tx.start_time = time.time()
            self.active_transactions.add(tx_id)

            # 记录日志
            self._write_log(tx_id, "begin", {"isolation_level": isolation_level.name})

            if self.logger:
                self.logger.log_operation("事务", f"开始事务 {tx_id}")

            return tx_id

    def add_operation(self, tx_id: str, operation: Operation) -> bool:
        """
        向事务添加操作

        Args:
            tx_id: 事务ID
            operation: 操作对象

        Returns:
            是否成功添加
        """
        with self.tx_lock:
            tx = self.transactions.get(tx_id)
            if not tx or tx.state != TransactionState.ACTIVE:
                return False

            # 检查隔离级别约束
            if not self._check_isolation(tx, operation):
                return False

            # 获取资源锁
            if operation.target and not self._acquire_lock(tx_id, operation.target, tx.isolation_level):
                return False

            # 创建备份（用于回滚）
            if operation.op_type in ["update", "delete"]:
                self._create_backup(operation)

            tx.operations.append(operation)

            if self.logger:
                self.logger.log_operation(
                    f"事务 {tx_id}",
                    f"添加操作: {operation.op_type} {operation.target}"
                )

            return True

    def create_savepoint(self, tx_id: str, savepoint_name: str) -> bool:
        """
        创建保存点

        Args:
            tx_id: 事务ID
            savepoint_name: 保存点名称

        Returns:
            是否成功创建
        """
        with self.tx_lock:
            tx = self.transactions.get(tx_id)
            if not tx or tx.state != TransactionState.ACTIVE:
                return False

            # 记录当前操作位置
            tx.savepoints[savepoint_name] = len(tx.operations)

            if self.logger:
                self.logger.log_operation(
                    f"事务 {tx_id}",
                    f"创建保存点: {savepoint_name}"
                )

            return True

    def rollback_to_savepoint(self, tx_id: str, savepoint_name: str) -> bool:
        """
        回滚到保存点

        Args:
            tx_id: 事务ID
            savepoint_name: 保存点名称

        Returns:
            是否成功回滚
        """
        with self.tx_lock:
            tx = self.transactions.get(tx_id)
            if not tx or tx.state != TransactionState.ACTIVE:
                return False

            if savepoint_name not in tx.savepoints:
                return False

            # 获取保存点位置
            savepoint_index = tx.savepoints[savepoint_name]

            # 回滚保存点之后的操作
            operations_to_rollback = tx.operations[savepoint_index:]
            for op in reversed(operations_to_rollback):
                self._rollback_operation(op)

            # 截断操作列表
            tx.operations = tx.operations[:savepoint_index]

            # 释放相关锁
            for op in operations_to_rollback:
                if op.target in tx.locks:
                    tx.locks.remove(op.target)
                    self._release_lock(tx_id, op.target)

            if self.logger:
                self.logger.log_operation(
                    f"事务 {tx_id}",
                    f"回滚到保存点: {savepoint_name}"
                )

            return True

    def commit(self, tx_id: str) -> bool:
        """
        提交事务

        Args:
            tx_id: 事务ID

        Returns:
            是否成功提交
        """
        with self.tx_lock:
            tx = self.transactions.get(tx_id)
            if not tx or tx.state != TransactionState.ACTIVE:
                return False

            tx.state = TransactionState.COMMITTING

            try:
                # 执行所有操作
                for operation in tx.operations:
                    self._execute_operation(operation)

                # 写入日志
                if self.enable_wal:
                    self._write_log(tx_id, "commit", {
                        "operations": len(tx.operations),
                        "duration": time.time() - tx.start_time
                    })

                # 更新事务状态
                tx.state = TransactionState.COMMITTED
                tx.commit_time = time.time()
                self.active_transactions.discard(tx_id)

                # 释放所有锁
                self._release_all_locks(tx_id)

                # 清理备份
                self._cleanup_backups(tx_id)

                if self.logger:
                    self.logger.log_operation(
                        f"事务 {tx_id}",
                        f"提交成功: {len(tx.operations)} 个操作"
                    )

                return True

            except Exception as e:
                # 提交失败，回滚
                tx.state = TransactionState.FAILED
                self.rollback(tx_id)

                if self.logger:
                    self.logger.log_error(f"事务 {tx_id} 提交失败: {e}")

                return False

    def rollback(self, tx_id: str) -> bool:
        """
        回滚事务

        Args:
            tx_id: 事务ID

        Returns:
            是否成功回滚
        """
        with self.tx_lock:
            tx = self.transactions.get(tx_id)
            if not tx:
                return False

            if tx.state in [TransactionState.COMMITTED, TransactionState.ABORTED]:
                return False

            tx.state = TransactionState.ABORTING

            try:
                # 逆序回滚所有操作
                for operation in reversed(tx.operations):
                    self._rollback_operation(operation)

                # 写入日志
                if self.enable_wal:
                    self._write_log(tx_id, "rollback", {
                        "operations": len(tx.operations)
                    })

                # 更新状态
                tx.state = TransactionState.ABORTED
                self.active_transactions.discard(tx_id)

                # 释放所有锁
                self._release_all_locks(tx_id)

                # 恢复备份
                self._restore_backups(tx_id)

                if self.logger:
                    self.logger.log_operation(
                        f"事务 {tx_id}",
                        f"回滚成功: {len(tx.operations)} 个操作"
                    )

                return True

            except Exception as e:
                tx.state = TransactionState.FAILED

                if self.logger:
                    self.logger.log_error(f"事务 {tx_id} 回滚失败: {e}")

                return False

    def _execute_operation(self, operation: Operation):
        """
        执行操作

        Args:
            operation: 操作对象
        """
        if operation.op_type == "create":
            # 创建文件或资源
            if operation.target and operation.new_value:
                Path(operation.target).parent.mkdir(parents=True, exist_ok=True)
                with open(operation.target, 'w', encoding='utf-8') as f:
                    f.write(str(operation.new_value))

        elif operation.op_type == "update":
            # 更新文件或资源
            if operation.target and operation.new_value:
                with open(operation.target, 'w', encoding='utf-8') as f:
                    f.write(str(operation.new_value))

        elif operation.op_type == "delete":
            # 删除文件或资源
            if operation.target and os.path.exists(operation.target):
                os.remove(operation.target)

        elif operation.op_type == "rename":
            # 重命名文件或资源
            if operation.old_value and operation.new_value:
                os.rename(operation.old_value, operation.new_value)

    def _rollback_operation(self, operation: Operation):
        """
        回滚操作

        Args:
            operation: 操作对象
        """
        if operation.op_type == "create":
            # 删除创建的文件
            if operation.target and os.path.exists(operation.target):
                os.remove(operation.target)

        elif operation.op_type == "update":
            # 恢复原值
            if operation.target and operation.old_value is not None:
                with open(operation.target, 'w', encoding='utf-8') as f:
                    f.write(str(operation.old_value))

        elif operation.op_type == "delete":
            # 恢复删除的文件
            if operation.target and operation.rollback_data:
                Path(operation.target).parent.mkdir(parents=True, exist_ok=True)
                with open(operation.target, 'wb') as f:
                    f.write(operation.rollback_data)

        elif operation.op_type == "rename":
            # 恢复原名
            if operation.old_value and operation.new_value:
                if os.path.exists(operation.new_value):
                    os.rename(operation.new_value, operation.old_value)

    def _check_isolation(self, tx: Transaction, operation: Operation) -> bool:
        """
        检查隔离级别约束

        Args:
            tx: 事务对象
            operation: 操作对象

        Returns:
            是否满足隔离级别
        """
        if tx.isolation_level == IsolationLevel.SERIALIZABLE:
            # 串行化级别，检查是否有其他活动事务
            if len(self.active_transactions) > 1:
                # 需要等待其他事务完成
                return False

        elif tx.isolation_level == IsolationLevel.REPEATABLE_READ:
            # 可重复读，检查是否有其他事务修改了相同资源
            if operation.target in self.lock_holders:
                holder_tx_id = self.lock_holders[operation.target]
                if holder_tx_id != tx.tx_id:
                    return False

        return True

    def _acquire_lock(self, tx_id: str, resource: str, isolation_level: IsolationLevel) -> bool:
        """
        获取资源锁

        Args:
            tx_id: 事务ID
            resource: 资源标识
            isolation_level: 隔离级别

        Returns:
            是否成功获取锁
        """
        with self.lock_manager:
            # 检查是否已被其他事务持有
            if resource in self.lock_holders:
                holder = self.lock_holders[resource]
                if holder != tx_id:
                    # 根据隔离级别决定是否等待
                    if isolation_level >= IsolationLevel.REPEATABLE_READ:
                        return False  # 不等待，直接失败
                    # READ_COMMITTED可以等待
                    # 这里简化处理，实际应该实现等待队列

            # 获取或创建锁
            if resource not in self.resource_locks:
                self.resource_locks[resource] = threading.RLock()

            # 记录锁持有者
            self.lock_holders[resource] = tx_id

            # 添加到事务锁集合
            tx = self.transactions.get(tx_id)
            if tx:
                tx.locks.add(resource)

            return True

    def _release_lock(self, tx_id: str, resource: str):
        """
        释放资源锁

        Args:
            tx_id: 事务ID
            resource: 资源标识
        """
        with self.lock_manager:
            if resource in self.lock_holders and self.lock_holders[resource] == tx_id:
                del self.lock_holders[resource]

    def _release_all_locks(self, tx_id: str):
        """
        释放事务持有的所有锁

        Args:
            tx_id: 事务ID
        """
        with self.lock_manager:
            tx = self.transactions.get(tx_id)
            if tx:
                for resource in tx.locks:
                    self._release_lock(tx_id, resource)
                tx.locks.clear()

    def _create_backup(self, operation: Operation):
        """
        创建备份

        Args:
            operation: 操作对象
        """
        if operation.target and os.path.exists(operation.target):
            # 读取当前内容
            with open(operation.target, 'rb') as f:
                operation.rollback_data = f.read()

            # 如果是更新操作，保存旧值
            if operation.op_type == "update":
                with open(operation.target, 'r', encoding='utf-8') as f:
                    operation.old_value = f.read()

    def _restore_backups(self, tx_id: str):
        """
        恢复备份

        Args:
            tx_id: 事务ID
        """
        backup_path = self.backup_dir / tx_id
        if backup_path.exists():
            # 恢复所有备份文件
            for backup_file in backup_path.iterdir():
                original_path = backup_file.stem
                shutil.copy2(backup_file, original_path)

    def _cleanup_backups(self, tx_id: str):
        """
        清理备份

        Args:
            tx_id: 事务ID
        """
        backup_path = self.backup_dir / tx_id
        if backup_path.exists():
            shutil.rmtree(backup_path)

    def _write_log(self, tx_id: str, action: str, details: Dict[str, Any] = None):
        """
        写入事务日志

        Args:
            tx_id: 事务ID
            action: 动作
            details: 详细信息
        """
        if not self.enable_wal:
            return

        log = TransactionLog(
            log_id=f"log_{len(self.tx_logs)}",
            tx_id=tx_id,
            action=action,
            timestamp=time.time(),
            details=details or {}
        )

        self.tx_logs.append(log)

        # 持久化到文件
        with open(self.log_file, 'a', encoding='utf-8') as f:
            log_data = {
                'log_id': log.log_id,
                'tx_id': log.tx_id,
                'action': log.action,
                'timestamp': log.timestamp,
                'details': log.details
            }
            f.write(json.dumps(log_data) + '\n')

    def _load_transaction_log(self):
        """加载事务日志"""
        if not self.log_file.exists():
            return

        try:
            with open(self.log_file, 'r', encoding='utf-8') as f:
                for line in f:
                    if line.strip():
                        log_data = json.loads(line)
                        log = TransactionLog(
                            log_id=log_data['log_id'],
                            tx_id=log_data['tx_id'],
                            action=log_data['action'],
                            timestamp=log_data['timestamp'],
                            details=log_data.get('details', {})
                        )
                        self.tx_logs.append(log)
        except Exception as e:
            if self.logger:
                self.logger.log_error(f"加载事务日志失败: {e}")

    def create_checkpoint(self) -> str:
        """
        创建检查点

        Returns:
            检查点ID
        """
        checkpoint_id = f"checkpoint_{int(time.time())}"

        # 保存当前状态
        checkpoint_data = {
            'id': checkpoint_id,
            'timestamp': time.time(),
            'active_transactions': list(self.active_transactions),
            'committed_count': len([tx for tx in self.transactions.values()
                                   if tx.state == TransactionState.COMMITTED])
        }

        checkpoint_file = self.workspace_dir / f"{checkpoint_id}.json"
        with open(checkpoint_file, 'w', encoding='utf-8') as f:
            json.dump(checkpoint_data, f, indent=2)

        self.checkpoints.append(checkpoint_id)

        if self.logger:
            self.logger.log_operation("检查点", f"创建检查点: {checkpoint_id}")

        return checkpoint_id

    def get_statistics(self) -> Dict[str, Any]:
        """
        获取统计信息

        Returns:
            统计信息字典
        """
        with self.tx_lock:
            total_tx = len(self.transactions)
            active = len(self.active_transactions)
            committed = len([tx for tx in self.transactions.values()
                           if tx.state == TransactionState.COMMITTED])
            aborted = len([tx for tx in self.transactions.values()
                         if tx.state == TransactionState.ABORTED])
            failed = len([tx for tx in self.transactions.values()
                        if tx.state == TransactionState.FAILED])

            total_ops = sum(len(tx.operations) for tx in self.transactions.values())
            avg_ops = total_ops / total_tx if total_tx > 0 else 0

            return {
                'total_transactions': total_tx,
                'active': active,
                'committed': committed,
                'aborted': aborted,
                'failed': failed,
                'total_operations': total_ops,
                'avg_operations_per_tx': avg_ops,
                'locks_held': len(self.lock_holders),
                'checkpoints': len(self.checkpoints),
                'log_entries': len(self.tx_logs)
            }

    def cleanup(self):
        """清理资源"""
        # 回滚所有活动事务
        for tx_id in list(self.active_transactions):
            self.rollback(tx_id)

        # 清理工作空间
        if self.workspace_dir.exists():
            shutil.rmtree(self.workspace_dir)